

#include <vector>
#include <iostream>
#include <string>
#include <signal.h>
#include <cstdarg>
#include <algorithm>

using namespace std;

static std::vector<std::string> key_table, value_table;

// the following are UBUNTU/LINUX ONLY terminal color codes.
#define RESET "\033[0m"
#define BLACK "\033[30m"              /* Black */
#define RED "\033[31m"                /* Red */
#define GREEN "\033[32m"              /* Green */
#define YELLOW "\033[33m"             /* Yellow */
#define BLUE "\033[34m"               /* Blue */
#define MAGENTA "\033[35m"            /* Magenta */
#define CYAN "\033[36m"               /* Cyan */
#define WHITE "\033[37m"              /* White */
#define BOLDBLACK "\033[1m\033[30m"   /* Bold Black */
#define BOLDRED "\033[1m\033[31m"     /* Bold Red */
#define BOLDGREEN "\033[1m\033[32m"   /* Bold Green */
#define BOLDYELLOW "\033[1m\033[33m"  /* Bold Yellow */
#define BOLDBLUE "\033[1m\033[34m"    /* Bold Blue */
#define BOLDMAGENTA "\033[1m\033[35m" /* Bold Magenta */
#define BOLDCYAN "\033[1m\033[36m"    /* Bold Cyan */
#define BOLDWHITE "\033[1m\033[37m"   /* Bold White */

static void atexit_reset_console(void)
{
    std::cout << BLUE << "\nRedis@system  ~  " << YELLOW << "Exit!\n"
              << RESET << std::endl;
}

static bool isRun = true;

static void atsigInt_reset_console(int)
{
    if (isRun == false)
        return;
    atexit_reset_console();
    isRun = false;
    exit(0);
}

template <typename T>
void sysPrintln(T line)
{
    std::cout << BLUE << "Redis@system   ~ " << YELLOW << line << RESET << std::endl;
}

enum class LOG_LEVEL
{
    INFO,
    ERROR,
    WARNING,
    FAILD
};

std::string to_string(LOG_LEVEL level)
{

    switch (level)
    {
    case LOG_LEVEL::INFO:
        return "INFO";
    case LOG_LEVEL::ERROR:
        return "ERROR";
    case LOG_LEVEL::WARNING:
        return "WARNING";
    case LOG_LEVEL::FAILD:
        return "FAILD";
    }
    return "INFO";
}

std::string to_color(LOG_LEVEL level)
{

    switch (level)
    {
    case LOG_LEVEL::INFO:
        return BLUE;
    case LOG_LEVEL::ERROR:
        return RED;
    case LOG_LEVEL::WARNING:
        return YELLOW;
    case LOG_LEVEL::FAILD:
        return BOLDRED;
    }
    return BLUE;
}

void syslog(LOG_LEVEL level, const string &log)
{
    string strLevel = to_string(level);
    string color = to_color(level);
    if (level == LOG_LEVEL::ERROR || level == LOG_LEVEL::FAILD)
    {
        std::cerr << color << "[" << strLevel << "] : " << log << RESET << std::endl;
        return;
    }
    std::cout << color << "[" << strLevel << "] : " << log << RESET << std::endl;
}

void sysPrintln(std::vector<std::string> res)
{
    std::cout << BLUE << "Redis@system   ~ " << YELLOW;

    std::cout << "[";
    for (auto &&i : res)
    {
        if (i != res[0])
        {
            std::cout << ", ";
        }
        std::cout << i;
    }
    std::cout << "]";

    std::cout << RESET << std::endl;
}

std::vector<std::string> split(const std::string &str, char split)
{
    std::vector<std::string> res;

    if (str.empty())
        return res;
    std::string strs = str + split;
    size_t pos = strs.find(split);
    while (pos != std::string::npos)
    {
        std::string temp = strs.substr(0, pos);
        if (temp != "")
            res.push_back(temp);
        strs = strs.substr(pos + 1);
        pos = strs.find(split);
    }
    return res;
}

void showConsole(void)
{
    std::cout << GREEN << "Redis@Console >> " << RESET;
    std::cout.flush();
}

void proc_line(std::vector<std::string> &res)
{
    auto count = res.size();
    auto &cmd = res[0];
    if (cmd == "insert")
    {
        if (count < 3)
        {
            syslog(LOG_LEVEL::ERROR, "insert [key] [value]");
            return;
        }

        auto pos = find(key_table.begin(), key_table.end(), res[1]);
        if (pos != key_table.end())
        {
            syslog(LOG_LEVEL::FAILD, "The key value already exists");
            return;
        }
        key_table.push_back(res[1]);
        value_table.push_back(res[2]);
        sysPrintln("insert sueccs");
        return;
    }

    if (cmd == "get")
    {

        if (count < 2)
        {
            syslog(LOG_LEVEL::ERROR, "get [key]");
            return;
        }

        auto pos = find(key_table.begin(), key_table.end(), res[1]);
        if (pos == key_table.end())
        {
            syslog(LOG_LEVEL::INFO, "The key value does not exist");
            return;
        }

        sysPrintln(value_table[pos - key_table.begin()]);

        return;
    }

    if (cmd == "update")
    {

        if (count < 3)
        {
            syslog(LOG_LEVEL::ERROR, "get [key]");
            return;
        }

        auto pos = find(key_table.begin(), key_table.end(), res[1]);
        if (pos == key_table.end())
        {
            syslog(LOG_LEVEL::INFO, "The key value does not exist");
            return;
        }

        value_table[pos - key_table.begin()] = res[2];
        sysPrintln("update sueccs");

        return;
    }

    if (cmd == "delete")
    {

        if (count < 2)
        {
            syslog(LOG_LEVEL::ERROR, "delete [key]");
            return;
        }

        auto pos = find(key_table.begin(), key_table.end(), res[1]);
        if (pos == key_table.end())
        {
            syslog(LOG_LEVEL::INFO, "delete NULL");
            return;
        }

        auto vpos = value_table.begin() + (pos - key_table.begin());

        key_table.erase(pos);
        value_table.erase(vpos);
        sysPrintln("delete sueccs");

        return;
    }

    if (cmd == "list")
    {
        std::cout << BLUE << "Redis@system   ~ {" << RESET << std::endl;
        for (int i = 0; i < key_table.size(); i++)
        {

            std::cout << "\t{ " << YELLOW << key_table[i] << RESET << ", " << YELLOW << value_table[i] << RESET << " }," << std::endl;
        }
        std::cout << BLUE << "}\n"
                  << RESET << std::endl;

        return;
    }

    if (cmd == "exit" || cmd == "quit")
    {
        atexit_reset_console();
        exit(0);
        return;
    }
}

int main(int argc, char const *argv[])
{
    signal(SIGINT, atsigInt_reset_console);

    std::cout << GREEN << "Redis@Console >> " << RESET;
    while (true)
    {
        std::string line;

        std::getline(std::cin, line);
        if (isRun == false)
            break;
        auto res = split(line, ' ');

        auto count = res.size();
        if (count == 0)
            continue;

        proc_line(res);
        showConsole();
    }

    cout << oct << 100 << endl;

    return 0;
}
